package utils
{
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;

	/**
	 * 图形工具类
	 * 提供一些形状位图绘制，比如五角星
	 * 和图形的处理
	 * @author WynnLam
	 * 
	 */	
	public class GraphicsUtil
	{
		private static var _CACHE_STATE:int = 0;
		private static var _CACHE_GRAY:int = 1;
		/**
		 * 绘制位图结果的缓存，避免多次生成新位图对象造成内存浪费
		 */		
		private static var _cache:Array = [];
		
		public static var Mat:Matrix = new Matrix();//公共转换矩阵，用于优化
		public static var SourceRect:Rectangle = new Rectangle();//源矩形
		public static var DestPoint:Point = new Point();//目标点
		public static var ClrMatFilter:ColorMatrixFilter = new ColorMatrixFilter();//颜色矩阵滤镜
		//颜色转换矩阵
		public static var MatArr:Array = [
			1, 0, 0, 0, 0,
			0, 1, 0, 0, 0,
			0, 0, 1, 0, 0,
			0, 0, 0, 1, 0];
		
		/**
		 * 重置转换矩阵
		 * 
		 */		
		public static function resetMatrix():void
		{
			Mat.a = Mat.d = 1;
			Mat.b = Mat.c = 0;
			Mat.tx = Mat.ty = 0;
		}
		/**
		 * 重置颜色转换矩阵
		 * 
		 */		
		public static function resetMatrixArray():void
		{
			var w:int = 5;
			var h:int = 4;
			for(var i:int = 0; i < h; i++)
			{
				for(var j:int = 0; j < w; j++)
				{
					MatArr[i * w + j] = (i == j) ? 1 : 0;
				}
			}
		}
		
		/**
		 * 五角星
		 * 画出角度不断递增并且半径交替变化的一系列线条，就可以构成一个五角星
		 * 通过设置形状半径，可以改变星星的形状，比如当值和起始半径相等时，星星变成了一个等边形
		 * 形状半径还可以改变星星的尺寸，比如当值等于起始半径2倍时，星星就会比原来大一倍
		 * @param radius 起始半径，即中心点到起始点的距离
		 * @param radius2 形状半径
		 * @param lineColor 线条颜色
		 * @param fillColor 填充颜色
		 * @param degrees 起始角度，可以改变星星的朝向
		 * @return 
		 * 
		 */		
		public static function star(radius:Number, radius2:Number = NaN, lineColor:uint = 0xFFFF00, fillColor:uint = 0xFFFF00, degrees:Number = -90):BitmapData
		{
			//获取形状半径,默认为:起始半径/2
			if(!radius2) radius2 = radius / 2;
			//计算起始角度
			var startAngle:Number = degrees * Math.PI / 180;
			
			//创建一个临时Shape
			var shape:Shape = new Shape();
			shape.graphics.lineStyle(0, lineColor);
			shape.graphics.beginFill(fillColor);
			//设置起点
			shape.graphics.moveTo(Math.cos(startAngle) * radius, Math.sin(startAngle) * radius);
			//画10条线
			var r:Number;
			var angle:Number;
			for(var i:int = 1; i < 11; i++)
			{
				r = i % 2 > 0 ? radius2 : radius;
				angle = Math.PI * 2 / 10 * i + startAngle;
				shape.graphics.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
			}
			
			//计算星星的半径
			r = Math.max(radius, radius2);
			//设置矩阵
			resetMatrix();
			Mat.tx = Mat.ty = r;
			//创建一个和星星大小一致的BitmapData，把星星填充进去
			var bmpd:BitmapData = new BitmapData(r * 2, r * 2, true, 0);
			bmpd.draw(shape, Mat);
			
			return bmpd;
		}
		/**
		 * 绘制扇形
		 * @param graphics
		 * @param ox 扇形的圆心x坐标
		 * @param oy 扇形的圆心y坐标
		 * @param r 扇形的半径
		 * @param startDegree 扇形起始角度
		 * @param sectorAngle 扇形的角度
		 * @param color 扇形颜色
		 * @param alpha 扇形透明度，默认为60%
		 * 
		 */		
		public static function drawSector(graphics:Graphics, ox:Number, oy:Number, r:Number, startDegree:Number, sectorAngle:Number, color:uint, alpha:Number = 0.6):void
		{
			graphics.beginFill(color, alpha);
			graphics.moveTo(ox, oy);
			
			if (startDegree > 360)
				startDegree %= 360;
			startDegree = startDegree * Math.PI / 180;
			
			if (sectorAngle > 360)
				sectorAngle %= 360;
			var n:int = Math.ceil(Math.abs(sectorAngle) / 45);
			var angleA:Number = sectorAngle / n;
			angleA = angleA * Math.PI / 180;
			
			graphics.lineTo(ox + r * Math.cos(startDegree), oy + r * Math.sin(startDegree));
			for (var i:int = 0; i < n; i++)
			{
				startDegree += angleA;
				var angleMid:Number = startDegree - angleA / 2;
				var cx:Number = ox + r / Math.cos(angleA / 2) * Math.cos(angleMid);
				var cy:Number = oy + r / Math.cos(angleA / 2) * Math.sin(angleMid);
				var ax:Number = ox + r * Math.cos(startDegree);
				var ay:Number = oy + r * Math.sin(startDegree);
				graphics.curveTo(cx, cy, ax, ay);
			}
			if (sectorAngle != 360)
				graphics.lineTo(ox, oy);
			
			graphics.endFill();
		}
		/**
		 * 以bmpd填充一个网格
		 * @param bmpd 源位图
		 * @param rows 行数
		 * @param cols 列数
		 * @param rowSpace 行距
		 * @param colSpace 列距
		 * @param hollowOut 镂空的格子，一个长度为rows * cols的数组，1表示镂空，0表示不镂空
		 * @return 
		 * 
		 */		
		public static function drawGrid(bmpd:BitmapData, rows:int, cols:int, rowSpace:int = 0, colSpace:int = 0, hollowOut:Array = null):BitmapData
		{
			resetMatrix();
			var mat:Matrix = Mat;
			
			var cw:Number = bmpd.width;
			var ch:Number = bmpd.height;
			var w:int = (cw + colSpace) * cols - colSpace;
			var h:int = (ch + rowSpace) * rows - rowSpace;
			var targetBmpd:BitmapData = new BitmapData(w, h, true, 0);
			for(var r:int = 0; r < rows; r++)
			{
				mat.ty = r * (ch + rowSpace);
				for(var c:int = 0; c < cols; c++)
				{
					if(hollowOut && hollowOut[r * cols + c])
						continue;
					mat.tx = c * (cw + colSpace);
					targetBmpd.draw(bmpd, mat);
				}
			}
			return targetBmpd;
		}
		/**
		 * 从源位图数据生成高亮位图数据
		 * @param srcBmpd
		 * @return 
		 * 
		 */		
		public static function createOverStateBmpd(srcBmpd:BitmapData):BitmapData
		{
			return createStateBmpd(srcBmpd, 50);
		}
		/**
		 * 从源位图数据生成灰暗位图数据
		 * @param srcBmpd
		 * @return 
		 * 
		 */		
		public static function createDownStateBmpd(srcBmpd:BitmapData):BitmapData
		{
			return createStateBmpd(srcBmpd, -50);
		}
		/**
		 * 从源位图数据和指定的亮度生成新的位图数据
		 * @param srcBmpd
		 * @param brightness
		 * @return 
		 * 
		 */		
		public static function createStateBmpd(srcBmpd:BitmapData, brightness:Number):BitmapData
		{
			var stateCache:Dictionary = _cache[_CACHE_STATE];
			if(!stateCache) stateCache = _cache[_CACHE_STATE] = new Dictionary();
			var bmdCache:Dictionary = stateCache[srcBmpd];
			if(!bmdCache) bmdCache = stateCache[srcBmpd] = new Dictionary();
			var bmpd:BitmapData = bmdCache[brightness] as BitmapData;
			if(bmpd) return bmpd;
			
			SourceRect.x = SourceRect.y = 0;
			SourceRect.width = srcBmpd.width;
			SourceRect.height = srcBmpd.height;
			
			resetMatrixArray();
			MatArr[4] = MatArr[9] = MatArr[14] = brightness;
			ClrMatFilter.matrix = MatArr;
			
			bmpd = new BitmapData(srcBmpd.width, srcBmpd.height, srcBmpd.transparent, 0);
			bmpd.applyFilter(srcBmpd, SourceRect, DestPoint, ClrMatFilter);
			bmdCache[brightness] = bmpd;
			return bmpd;
		}
		/**
		 * 从源位图数据生成灰度位图数据
		 * @param srcBmpd
		 * @return 
		 * 
		 */		
		public static function createGrayBmpd(srcBmpd:BitmapData):BitmapData
		{
			var bmdCache:Dictionary = _cache[_CACHE_GRAY];
			if(!bmdCache) bmdCache = _cache[_CACHE_GRAY] = new Dictionary();
			var bmpd:BitmapData = bmdCache[srcBmpd] as BitmapData;
			if(bmpd) return bmpd;
			
			SourceRect.x = SourceRect.y = 0;
			SourceRect.width = srcBmpd.width;
			SourceRect.height = srcBmpd.height;
			
			ClrMatFilter.matrix = [
				0.3086, 0.6094, 0.082, 0, 0,
				0.3086, 0.6094, 0.082, 0, 0, 
				0.3086, 0.6094, 0.082, 0, 0, 
				0, 0, 0, 1, 0
			];
			
			bmpd = new BitmapData(srcBmpd.width, srcBmpd.height, srcBmpd.transparent, 0);
			bmpd.applyFilter(srcBmpd, SourceRect, DestPoint, ClrMatFilter);
			bmdCache[srcBmpd] = bmpd;
			return bmpd;
		}
		/**
		 * 从源位图数据生成平移位图数据
		 * @param srcBmpd
		 * @param dx
		 * @param dy
		 * @return 
		 * 
		 */		
		public static function createTranslateBmpd(srcBmpd:BitmapData, dx:Number=0, dy:Number=0):BitmapData
		{
			resetMatrix();
			Mat.translate(dx, dy);
			
			var bmpd:BitmapData = new BitmapData(srcBmpd.width + dx, srcBmpd.height + dy, srcBmpd.transparent, 0);
			bmpd.draw(srcBmpd, Mat);
			
			return bmpd;
		}
		/**
		 * 水平翻转源位图数据生成新的位图数据
		 * @param srcBmpd
		 * @return 
		 * 
		 */		
		public static function flipBmpdHorizontal(srcBmpd:BitmapData):BitmapData
		{
			//获取位图尺寸
			var w:int = srcBmpd.width;
			var h:int = srcBmpd.height;
			
			//设置位图区域矩形
			SourceRect.x = SourceRect.y = 0;
			SourceRect.width = w;
			SourceRect.height = h;
			
			//获取源位图像素向量
			var vec:Vector.<uint> = srcBmpd.getVector(SourceRect);
			//水平翻转所需的宽度
			var halfW:int = w >> 1;
			//向量的起始索引
			var start:int;
			//向量当前索引
			var index:int;
			//翻转索引
			var reIndex:int;
			//临时像素值
			var temp:uint;
			//翻转向量中的像素值
			for(var y:int = 0; y < h; y++)
			{
				start = y * w;
				for(var x:int = 0; x < halfW; x++)
				{
					index = start + x;
					reIndex = start + w - x - 1;
					temp = vec[index];
					vec[index] = vec[reIndex];
					vec[reIndex] = temp;
				}
			}
			
			//创建新的尺寸和透明属性跟源位图一致的位图
			var bmpd:BitmapData = new BitmapData(w, h, srcBmpd.transparent, 0);
			//填充翻转后的像素
			bmpd.setVector(SourceRect, vec);
			
			return bmpd;
		}
		/**
		 * 垂直翻转位图，并生成新位图
		 * @param srcBmpd
		 * @return 
		 * 
		 */		
		public static function flipBmpdVertical(srcBmpd:BitmapData):BitmapData
		{
			//获取位图尺寸
			var w:int = srcBmpd.width;
			var h:int = srcBmpd.height;
			
			//设置位图区域矩形
			SourceRect.x = SourceRect.y = 0;
			SourceRect.width = w;
			SourceRect.height = h;
			
			//获取源位图像素向量
			var vec:Vector.<uint> = srcBmpd.getVector(SourceRect);
			//颠倒像素
			vec.reverse();
			
			//创建新的尺寸和透明属性跟源位图一致的位图
			var bmpd:BitmapData = new BitmapData(w, h, srcBmpd.transparent, 0);
			//填充翻转后的像素
			bmpd.setVector(SourceRect, vec);
			
			return bmpd;
		}
		
		/**
		 * 绘制位图超出alpha门限值的像素到Graphics上，
		 * 可以用作实现鼠标不规则点击区域和不规则遮罩等等。
		 * @param graphics
		 * @param bmpd
		 * @param threshold	alpha阙值
		 */		
		public static function drawBmpdArea(graphics:Graphics, bmpd:BitmapData, threshold:uint=0):void
		{
			var w:int = bmpd.width;
			var h:int = bmpd.height;
			graphics.beginFill(0, 0);
			for(var i:int=0; i<w; i++)
			{
				for(var j:int=0; j<h; j++)
				{
					var alphaValue:uint = bmpd.getPixel32(i, j) >>> 24;
					if(alphaValue > threshold)
					{
						graphics.drawRect(i, j, 1, 1);
					}
				}
			}
			graphics.endFill();
		}
	}
}